<?php
/**
 * @author Daybreaky
 * @file ExceptionHandlerTrait.php
 * @info PhpStorm
 * @date 2022/4/16
 */

namespace App\Api\Traits;

use App\Api\Exceptions\BusinessException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException;
use Throwable;

trait ExceptionHandlerTrait
{
    use BusinessExceptionTrait, ApiResponseTrait;

    /**
     * @param $request
     * @param Throwable $exception
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
     * @throws Throwable
     */
    public function render($request, Throwable $exception): \Illuminate\Http\Response|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
    {
        // 如果是生产环境则返回500
        if (!config('app.debug')) {
            $this->throwBusinessException(Arr::get(config('api-response.error_code'), 'code') ?: 500, Arr::get(config('api-response.error_code'), 'message') ?: $exception->getMessage());
        }

        // 自定义错误异常抛出
        if ($exception instanceof BusinessException) {
            return $this->error($exception->getMessage(), $exception->getCode());
        }
        return parent::render($request, $exception);
    }

    /**
     * Custom Failed Validation Response for Laravel.
     *
     * @param Request $request
     * @param ValidationException $exception
     * @return JsonResponse
     */
    protected function invalidJson($request, ValidationException $exception)
    {
        return $this->error(
            message: Arr::get(config('api-response.exception'), ValidationException::class . 'message') ?: $exception->validator->errors()->first(), code: Arr::get(config('api-response.exception'), ValidationException::class . 'code') ?: 400, error: $exception->errors(),
        );
    }

    /**
     * Custom Failed Authentication Response for Laravel.
     *
     * @param Request $request
     * @param AuthenticationException $exception
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
     */
    protected function unauthenticated($request, AuthenticationException $exception)
    {
        return $request->expectsJson()
            ? $this->error(Arr::get(config('api-response.exception'), AuthenticationException::class . 'message') ?: $exception->getMessage())
            : redirect()->guest($exception->redirectTo() ?? route('login'));
    }

    /**
     * Custom Normal Exception response.
     *
     * @param $request
     * @param Throwable|Exception $e
     * @return JsonResponse
     */
    protected function prepareJsonResponse($request, $e)
    {
        // 要求请求头 header 中包含 /json 或 +json，如：Accept:application/json
        // 或者是 ajax 请求，header 中包含 X-Requested-With：XMLHttpRequest;
        $exceptionConfig = Arr::get(config('api-response.exception'), get_class($e));
        $isHttpException = $this->isHttpException($e);

        $message = $exceptionConfig['message'] ?? ($isHttpException ? $e->getMessage() : 'Server Error');
        $code = $exceptionConfig['code'] ?? ($isHttpException ? $e->getStatusCode() : 500);
        $header = $exceptionConfig['header'] ?? ($isHttpException ? $e->getHeaders() : []);
        $options = $exceptionConfig['options'] ?? (JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

        return $this->error($message, $code, $this->convertExceptionToArray($e), $header, $options);
    }
}
